/*
 * acooly.cn Inc.
 * Copyright (c) 2016 All Rights Reserved.
 * create by zhike
 * date:2016年4月4日
 *
 */
package com.acooly.module.openapi.client.provider.allinpay.marshall;

import com.acooly.core.common.exception.BusinessException;
import com.acooly.core.utils.validate.Validators;
import com.acooly.module.openapi.client.api.exception.ApiClientException;
import com.acooly.module.openapi.client.provider.allinpay.AllinpayConstants;
import com.acooly.module.openapi.client.provider.allinpay.OpenAPIClientAllinpayProperties;
import com.acooly.module.openapi.client.provider.allinpay.domain.AllinpayApiMessage;
import com.acooly.module.openapi.client.provider.allinpay.domain.AllinpayRequest;
import com.acooly.module.openapi.client.provider.allinpay.domain.AllinpayRequestInfo;
import com.acooly.module.openapi.client.provider.allinpay.utils.XmlUtils;
import com.acooly.module.safety.Safes;
import com.acooly.module.safety.key.KeyLoadManager;
import com.acooly.module.safety.signature.SignTypeEnum;
import com.acooly.module.safety.signature.SignerFactory;
import com.acooly.module.safety.support.KeyStoreInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.SortedMap;

/**
 * @author zhike
 */
@Slf4j
public class AllinpayMarshallSupport {

    @Autowired
    protected OpenAPIClientAllinpayProperties openAPIClientAllinpayProperties;

    @Autowired
    protected SignerFactory signerFactory;

    @Autowired
    private KeyLoadManager keyStoreLoadManager;


    protected OpenAPIClientAllinpayProperties getProperties() {
        return openAPIClientAllinpayProperties;
    }

    public SignerFactory getSignerFactory() {
        return signerFactory;
    }

    protected String doMarshall(AllinpayRequest source) {
        doVerifyParam(source);
        String signXml = XmlUtils.toXml(source,true);
        AllinpayRequestInfo requestInfo = source.getRequestInfo();
        requestInfo.setSignedMsg(doSign(signXml));
        String requestXml = XmlUtils.toXml(source,false);
        return requestXml;
    }

    /**
     * 校验参数
     *
     * @param source
     */
    protected void doVerifyParam(AllinpayApiMessage source) {
        try {
            source.doCheck();
            Validators.assertJSR303(source);
        } catch (Exception e) {
            throw new ApiClientException(e.getMessage());
        }
    }

    protected String doSign(String waitForSign) {
        return Safes.getSigner(SignTypeEnum.Cert).sign(waitForSign, getKeyStore());
    }

    /**
     * 验签
     *
     * @param message
     * @return
     */
    protected void doVerifySign(String message) {
        if(message == null){
            throw new BusinessException("传入的验签报文为空");
        }
        int pre = message.indexOf("<SIGNED_MSG>");
        int suf = message.indexOf("</SIGNED_MSG>");
        if(pre == - 1 || suf == -1 || pre >= suf){
            throw new BusinessException("找不到签名信息");
        }
        String signedStr = message.substring(pre + 12, suf);
        String msgStr = message.substring(0, pre) + message.substring(suf + 13);
        Safes.getSigner(SignTypeEnum.Cert).verify(msgStr, getKeyStore(), signedStr);
    }

    /**
     * 验签
     *
     * @param signature
     * @param plain
     * @return
     */
    protected void doMd5VerifySign(SortedMap<String, String> plain, String signature) {
        String cusId = plain.get(AllinpayConstants.POS_PARTNER_KEY);
        String md5Key = getMd5Key(cusId);
        Safes.getSigner(AllinpayConstants.MD5_SIGNER_TYPE).verify(plain, md5Key, signature);
    }

    /**
     * 动态加载密钥
     * @param cusId
     * @return
     */
    protected String getMd5Key(String cusId) {
        try {
            return keyStoreLoadManager.load(cusId, AllinpayConstants.PROVIDER_NAME_MD5);
        } catch (Exception e) {
            log.info("cusId={}密钥加载失败,启用配置文件密钥", cusId);
            return getProperties().getMd5Key();
        }
    }
    protected KeyStoreInfo getKeyStore() {
        return keyStoreLoadManager.load(AllinpayConstants.PROVIDER_DEF_PRINCIPAL, AllinpayConstants.PROVIDER_NAME_CERT);
    }

    protected void beforeMarshall(AllinpayApiMessage message) {

    }

}
